

import sys
from time import sleep
import os, time, uuid, hashlib, configparser, subprocess, re, shutil


# MAC config
# mac_config_file = '/etc/motor_config.ini'
mac_config_file = '/home/bj/.config/motor_config.ini'

# 检查配置文件是否存在
# display_config_file = '/etc/bj_display_info.conf'
display_config_file = '/home/bj/.config/bj_display_info.conf'

sys_drm_path = "/sys/class/drm"
drm_card1_HDMI_A1_PATH = sys_drm_path + "/card1-HDMI-A-1"
drm_card1_HDMI_A2_PATH = sys_drm_path + "/card1-HDMI-A-2"

HASHED_A1_KEY = "Hashed_HDMI_A1"
HASHED_A2_KEY = "Hashed_HDMI_A2"

saved_hash_a1 = ""
saved_hash_a2 = ""

downloads_folder = os.path.expanduser('/home/bj/Downloads')
update_folder = os.path.expanduser('/home/bj/Downloads/bj-zsh-timer')


def find_connected_display_edid_path_list():
    drm_path = "/sys/class/drm"
    connected_devices = []
    for device in os.listdir(drm_path):
        status_path = os.path.join(drm_path, device, "status")
        if os.path.exists(status_path):
            with open(status_path, 'r') as status_file:
                status = status_file.read().strip()
                if status == "connected":
                    edid_path = os.path.join(drm_path, device, "edid")
                    if os.path.exists(edid_path):
                        connected_devices.append(edid_path)
    return connected_devices

def find_connected_display_is_connected(device_drm_path:str):
    status_path = os.path.join(device_drm_path, "status")
    
    if os.path.exists(status_path):
        with open(status_path, 'r') as status_file:
            status = status_file.read().strip()
            if status == "connected":
                return True
                    
    return False

def find_connected_display_edid_by_path(device_drm_path:str):
    status_path = os.path.join(device_drm_path, "status")
    connected_device = ""
    
    if os.path.exists(status_path):
        with open(status_path, 'r') as status_file:
            status = status_file.read().strip()
            if status == "connected":
                edid_path = os.path.join(device_drm_path, "edid")
                if os.path.exists(edid_path):
                    connected_device = connected_device + edid_path
                    
    return connected_device
    
def read_edid_file(edid_path):
    try:
        with open(edid_path, 'rb') as f:
            edid_data = f.read()
        return edid_data
    except FileNotFoundError:
        print(f"No EDID data found at {edid_path}")
        return None

def extract_hex_data(edid_data, start, end):
    if edid_data is None:
        return None

    data_slice = edid_data[start:end+1]
    hex_data = data_slice.hex()
    return hex_data


def read_config_file(file_path):
    config = configparser.ConfigParser()
    try:
        with open(file_path, 'r') as f:
            # 添加错误检查，确保文件至少包含一个部分标题
            line = f.readline()
            if line.strip() == '':
                print("Configuration file contains no section headers.")
                return None
            f.seek(0)  # 将文件指针重新定位到文件开头
            config.read_file(f)
            return config
    except FileNotFoundError:
        print(f"Configuration file not found: {file_path}")
        return None
    except configparser.MissingSectionHeaderError:
        print("Configuration file contains no section headers.")
        return None

def read_saved_hash(key):
    # 读取保存的哈希值
    config = configparser.ConfigParser()
    config.read(display_config_file)
    
    if config.has_option('HDMI_EDID', key):
        print(f"Hashed {key} option found in the configuration file.")
        return config['HDMI_EDID'][key]
    else:
        print(f"Hashed {key} option not found in the configuration file.")
        return False

def md5_hash(text):
    # 对文本进行 MD5 加密
    hashed_text = hashlib.md5(text.encode()).hexdigest()
    return hashed_text

def display_edid_md5_check():
    """_summary_  检测edid md5

    Returns:
        _type_: _description_
    """
    global saved_hash_a1
    global saved_hash_a2

    hdmi_a1_md5_result = ""
    hdmi_a2_md5_result = ""
    ret = True
    
    hdmi_first_check = 0

    # 显示屏检测
    connected_hdmi_a1 = find_connected_display_edid_by_path(drm_card1_HDMI_A1_PATH)
    connected_hdmi_a2 = find_connected_display_edid_by_path(drm_card1_HDMI_A2_PATH)
    
    if connected_hdmi_a1 != "":
        print(f"Found connected display EDID at: {connected_hdmi_a1}")
        edid_data = read_edid_file(connected_hdmi_a1)
        if edid_data:
            start_byte = 8
            end_byte = 15
            hex_data = extract_hex_data(edid_data, start_byte, end_byte)
            print(f"Hex data from byte {start_byte} to {end_byte}: {hex_data}")
            
            combined_data = hex_data
            hdmi_a1_md5_result = md5_hash(combined_data)
            print(f"MD5 hash of combined data: {hdmi_a1_md5_result}")

    if connected_hdmi_a2 != "":
        print(f"Found connected display EDID at: {connected_hdmi_a2}")
        edid_data = read_edid_file(connected_hdmi_a2)
        if edid_data:
            start_byte = 8
            end_byte = 15
            hex_data = extract_hex_data(edid_data, start_byte, end_byte)
            print(f"Hex data from byte {start_byte} to {end_byte}: {hex_data}")
            
            combined_data = hex_data
            hdmi_a2_md5_result = md5_hash(combined_data)
            print(f"MD5 hash of combined data: {hdmi_a2_md5_result}")


    if connected_hdmi_a1 == "" and connected_hdmi_a2 == "":
        ret = ret & False

    # 比较保存的哈希值和当前计算的哈希值
    if saved_hash_a1 != "":
        if saved_hash_a1 == hdmi_a1_md5_result:
            hdmi_first_check = 1
            ret  = ret & True
            print(f"ret11:{ret}")
        else:
            if saved_hash_a1 == hdmi_a2_md5_result:
                hdmi_first_check = 2
                ret  = ret & True
                print(f"ret21:{ret}")
            else:
                if connected_hdmi_a2 != "":
                    ret = ret & False
                    print(f"ret22:{ret}")
    
    if saved_hash_a2 != "":
        if hdmi_first_check == 1:  
            if connected_hdmi_a2 != "":
                if saved_hash_a2 == hdmi_a2_md5_result:
                    ret  = ret & True
                    print(f"ret31:{ret}")
                else:
                    ret = ret & False
                    print(f"ret41:{ret}")    
        elif hdmi_first_check == 2:
            if connected_hdmi_a1 != "":
                if saved_hash_a2 == hdmi_a1_md5_result:
                    ret  = ret & True
                    print(f"ret32:{ret}")
                else:
                    ret = ret & False
                    print(f"ret42:{ret}")
        else:
            if connected_hdmi_a1 != "":
                if saved_hash_a2 == hdmi_a1_md5_result:
                    hdmi_first_check = 1
                    ret  = ret & True
                    print(f"ret33:{ret}")
                else:
                    # ret = ret & False
                    # print(f"ret43:{ret}") 
                    if saved_hash_a2 == hdmi_a2_md5_result:
                        hdmi_first_check = 2
                        ret  = ret & True
                        print(f"ret34:{ret}")
                    else:
                        if connected_hdmi_a2 != "":
                            ret = ret & False
                            print(f"ret44:{ret}")
                        else:
                            ret = ret & False
                            print(f"ret54:{ret}")
                            
    if ret:
        print("MD5 值匹配，校验通过。")
        return True
    else:
        print("MD5 值不匹配，校验失败。")
        return False  

def display_edid_md5_check_start():
    """_summary_  检测edid md5

    Returns:
        _type_: _description_
    """
    global saved_hash_a1
    global saved_hash_a2
    
    is_first_time = True
    hdmi_a1_md5_result = ""
    hdmi_a2_md5_result = ""
    
    if os.path.exists(display_config_file):
        # 存在配置文件
        config = read_config_file(display_config_file)
        if config:
            print("Configuration file loaded successfully.")
            # 在此处使用配置对象执行其他操作
            saved_hash_a1 = read_saved_hash(HASHED_A1_KEY)
            saved_hash_a2 = read_saved_hash(HASHED_A2_KEY)

            if saved_hash_a1 == "" or saved_hash_a2 == "":
                is_first_time = True
            else:
                is_first_time = False
        else:
            print("Failed to load configuration file.")
            return False    
    else:
    # 配置文件未存在，第一次运行创建配置文件
        is_first_time = True

    # 显示屏检测
    connected_hdmi_a1 = find_connected_display_edid_by_path(drm_card1_HDMI_A1_PATH)
    connected_hdmi_a2 = find_connected_display_edid_by_path(drm_card1_HDMI_A2_PATH)
    
    if connected_hdmi_a1 != "":
        print(f"Found connected display EDID at: {connected_hdmi_a1}")
        edid_data = read_edid_file(connected_hdmi_a1)
        if edid_data:
            start_byte = 8
            end_byte = 15
            hex_data = extract_hex_data(edid_data, start_byte, end_byte)
            print(f"Hex data from byte {start_byte} to {end_byte}: {hex_data}")
            
            combined_data = hex_data
            hdmi_a1_md5_result = md5_hash(combined_data)
            print(f"MD5 hash of combined data: {hdmi_a1_md5_result}")

    if connected_hdmi_a2 != "":
        print(f"Found connected display EDID at: {connected_hdmi_a2}")
        edid_data = read_edid_file(connected_hdmi_a2)
        if edid_data:
            start_byte = 8
            end_byte = 15
            hex_data = extract_hex_data(edid_data, start_byte, end_byte)
            print(f"Hex data from byte {start_byte} to {end_byte}: {hex_data}")
            
            combined_data = hex_data
            hdmi_a2_md5_result = md5_hash(combined_data)
            print(f"MD5 hash of combined data: {hdmi_a2_md5_result}")


    if is_first_time:
        # 第一次运行，保存哈希值到配置文件
        config = configparser.ConfigParser()
        config['HDMI_EDID'] = {HASHED_A1_KEY: hdmi_a1_md5_result,
                               HASHED_A2_KEY: hdmi_a2_md5_result}
        
        with open(display_config_file, 'w') as configfile:
            config.write(configfile)

        print("第一次运行，已将哈希值保存到 config.ini 文件中。")
        return True
    else:
        # 比较保存的哈希值和当前计算的哈希值
        if saved_hash_a1 == hdmi_a1_md5_result or saved_hash_a1 == hdmi_a2_md5_result or saved_hash_a2 == hdmi_a1_md5_result or saved_hash_a2 == hdmi_a2_md5_result :
            print("MD5 值匹配，校验通过。")
            return True
        else:
            print("MD5 值不匹配，校验失败。")
            return False  

   
def switch_to_text_console(console_number):
    """_summary_ 切换到文本控制台

    Args:
        console_number (_type_): _description_
    """
    # 拼接命令字符串
    command = "echo 1|sudo -S chvt {}".format(console_number)
    # 执行命令
    subprocess.run(command, shell=True)        

def restart_OpenBar():
    result = subprocess.run(['gnome-extensions', 'disable', 'openbar@neuromorph'], capture_output=True, text=True)

    if result.returncode == 0:
        print("Command succeeded!")
    else:
        print("Command failed!")
        
    time.sleep(1)

    result = subprocess.run(['gnome-extensions', 'enable', 'openbar@neuromorph'], capture_output=True, text=True)

    if result.returncode == 0:
        print("Command succeeded!")
    else:
        print("Command failed!")

def get_lsusb_output():
    """Run the lsusb command and return its output."""
    result = subprocess.run(['lsusb'], stdout=subprocess.PIPE, text=True)
    return result.stdout

def parse_lsusb_output(output):
    """Parse the lsusb output and return a list of keyboard devices."""
    keyboards = []
    for line in output.splitlines():
        # Convert line to lower case for case-insensitive matching
        if 'keyboard' in line.lower():
            keyboards.append(line)
    return keyboards

def system_reboot():
    try:
        # Execute the reboot command
        result = subprocess.run(['reboot'], check=True)
        print("System is rebooting...")
    except subprocess.CalledProcessError as e:
        print(f"Failed to reboot: {e}")

def get_default_sink():
    """获取当前默认音频输出设备"""
    result = subprocess.run(['pactl', 'get-default-sink'], capture_output=True, text=True)
    return result.stdout.strip()

def list_sinks():
    """列出所有音频输出设备"""
    result = subprocess.run(['pactl', 'list', 'short', 'sinks'], capture_output=True, text=True)
    sinks = result.stdout.strip().split('\n')
    sink_list = []
    for sink in sinks:
        sink_info = sink.split('\t')
        sink_list.append((sink_info[0], sink_info[1]))
    return sink_list

def set_default_sink(sink_name):
    """设置默认音频输出设备"""
    subprocess.run(['pactl', 'set-default-sink', sink_name])

def audio_check_loop():
    # 获取当前默认音频输出设备
    current_default_sink = get_default_sink()
    print(f"当前默认音频输出设备: {current_default_sink}")
    
    # 列出所有音频输出设备
    sinks = list_sinks()
    print("可用音频输出设备:")
    for index, (sink_id, sink_name) in enumerate(sinks):
        print(f"{index}: {sink_name}")
    
    # 自动选择带有 "HDMI" 的音频输出设备，忽略大小写
    hdmi_sink_index = next((index for index, (_, sink_name) in enumerate(sinks) if "HDMI" in sink_name.upper()), None)
    
    if hdmi_sink_index is not None:
        new_default_sink = sinks[hdmi_sink_index][1]
        
        if new_default_sink == current_default_sink:
            print(f"{new_default_sink} == {current_default_sink}")
            return
        
        set_default_sink(new_default_sink)
        print(f"已自动设置带有 'HDMI' 的音频输出设备为默认: {new_default_sink}")
    else:
        print("未找到带有 'HDMI' 的音频输出设备。")


def clean_downloads_folder():
    # 检查文件夹是否为空
    if os.listdir(downloads_folder):
        print(f"{downloads_folder} is not empty. Deleting files...")

        # 遍历文件夹中的所有文件和子文件夹
        for filename in os.listdir(downloads_folder):
            file_path = os.path.join(downloads_folder, filename)

            try:
                # 如果是文件，则删除文件
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    os.unlink(file_path)
                # 如果是文件夹，则删除文件夹及其内容
                elif os.path.isdir(file_path):
                    shutil.rmtree(file_path)
            except Exception as e:
                print(f"Failed to delete {file_path}. Reason: {e}")

        print("All files in the Downloads folder have been deleted.")
    else:
        print(f"{downloads_folder} is empty.")

def check_update_folder():
  # 检查文件夹路径是否存在
    if not os.path.exists(update_folder):
        print(f"The folder {update_folder} does not exist.")
        return    
    
    # 检查文件夹是否为空
    if os.listdir(update_folder):
        print(f"{update_folder} is not empty. Deleting files...")
        subprocess.run(['rsync', '-a', '--remove-source-files', '/home/bj/Downloads/bj-zsh-timer/', '/usr/local/bin/'])
        subprocess.run(['rm', '-rf','/home/bj/Downloads/bj-zsh-timer'])
    else:
        print(f"{downloads_folder} is empty.")


def get_mac_address(interface='eth0'):
    try:
        with open(f'/sys/class/net/{interface}/address', 'r') as f:
            mac_address = f.read().strip()
        return mac_address
    except Exception as e:
        return str(e)
  
def mac_read_saved_hash():
    # 读取保存的哈希值
    config = configparser.ConfigParser()
    config.read(mac_config_file)
    
    if config.has_option('MAC', 'Hashed_MAC_Address'):
        print("Hashed MAC Address option found in the configuration file.")
        return config['MAC']['Hashed_MAC_Address']
    else:
        print("Hashed MAC Address option not found in the configuration file.")
        return False
   
    
def MD5_check():    
    # 检查配置文件是否存在
    config_file = mac_config_file
    if os.path.exists(config_file):
        config = read_config_file(config_file)
        if config:
            print("Configuration file loaded successfully.")
            # 在此处使用配置对象执行其他操作
            saved_hash = mac_read_saved_hash()
            if saved_hash == False:
                return False
            
            is_first_time = False
        else:
            print("Failed to load configuration file.")
            return False    

    else:
        is_first_time = True

    # 获取树莓派的 MAC 地址
    mac_address = get_mac_address()

    # 对 MAC 地址进行 MD5 加密
    md5_result = md5_hash(mac_address)

    if is_first_time:
        # 第一次运行，保存哈希值到配置文件
        config = configparser.ConfigParser()
        config['MAC'] = {'Hashed_MAC_Address': md5_result}
        with open(config_file, 'w') as configfile:
            config.write(configfile)
        # print("第一次运行，已将哈希值保存到 config.ini 文件中。")
        return True
    else:
        # 比较保存的哈希值和当前计算的哈希值
        if saved_hash == md5_result:
            # print("MD5 值匹配，校验通过。")
            return True
        else:
            # print("MD5 值不匹配，校验失败。")
            return False          
      

if __name__ == '__main__':
    step = 0
    hdmi_a1_connected = False
    hdmi_a2_connected = False
    hdmi_a1_hotplug = False
    hdmi_a2_hotplug = False
    

    while False == MD5_check():
        print("motor start check fail")
        # 切换到文本控制台7
        #switch_to_text_console(7)
        # sys.exit(-1)
        # time.sleep(3)
        system_reboot()
    
    # check_update_folder()
    # clean_downloads_folder()
    # restart_OpenBar()

    if hdmi_a1_hotplug == False and find_connected_display_is_connected(drm_card1_HDMI_A1_PATH) == True:
        hdmi_a1_connected = True
        
    if hdmi_a1_hotplug == False and find_connected_display_is_connected(drm_card1_HDMI_A2_PATH) == True:
        hdmi_a2_connected = True
    
    display_edid_md5_check_start()
    
    while True:  
        lsusb_output = get_lsusb_output()
        keyboards = parse_lsusb_output(lsusb_output)
        
        if keyboards:
            print("Detected keyboard devices:")            
            for keyboard in keyboards:
                print(keyboard)
                
            system_reboot()
        else:
            print("No keyboard devices detected.")
          
        # 检查是否打开系统设置界面
        subprocess.run(['bash', '/usr/local/bin/check_gnome_setting.sh'])             
        
        if step == 0:
            if hdmi_a1_hotplug == False and find_connected_display_is_connected(drm_card1_HDMI_A1_PATH) == True:
                hdmi_a1_connected = True
                
            if hdmi_a1_hotplug == False and find_connected_display_is_connected(drm_card1_HDMI_A2_PATH) == True:
                hdmi_a2_connected = True
            
            # 判断是否出现插拔
            if hdmi_a1_connected == True and find_connected_display_is_connected(drm_card1_HDMI_A1_PATH) == False:
                step = 1
                hdmi_a1_connected = False
                hdmi_a1_hotplug = True

            
            if hdmi_a2_connected == True and find_connected_display_is_connected(drm_card1_HDMI_A2_PATH) == False:
                step = 1
                hdmi_a2_connected = False
                hdmi_a2_hotplug = True
            
            if False == display_edid_md5_check():
                print("display check fail!!!")
                # 切换到文本控制台7
                switch_to_text_console(7)
            else:
                print("display check success!!!")
                switch_to_text_console(2)
                  
        elif step == 1:
            # 1. 检测到有插拔就检测被拔掉的屏幕edid 
            if hdmi_a1_hotplug == True:
                if find_connected_display_is_connected(drm_card1_HDMI_A1_PATH) == True:
                    hdmi_a1_connected = True
                    hdmi_a1_hotplug = False
                    
            if hdmi_a2_hotplug == True:
                if find_connected_display_is_connected(drm_card1_HDMI_A2_PATH) == True:
                    hdmi_a2_connected = True
                    hdmi_a2_hotplug = False
            
            if hdmi_a1_hotplug == False and hdmi_a2_hotplug == False:
                step = 0
                print(f"step = {step}")
                
            if False == display_edid_md5_check():
                print("display check fail!!!")
                # 切换到文本控制台7
                switch_to_text_console(7)
            else:
                print("display check success!!!")
                switch_to_text_console(2)  
                
            print(f"hotplug check, hdmi_a1_hotplug:{hdmi_a1_hotplug}, hdmi_a2_hotplug:{hdmi_a2_hotplug}")

        lsusb_output = get_lsusb_output()
        keyboards = parse_lsusb_output(lsusb_output)

        audio_check_loop()
 
        time.sleep(1)
    

